﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PostSharp.Aspects;
using System.Reflection;
using System.Threading;
using System.Diagnostics;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
using Newtonsoft.Json;

namespace GrayParrot.Infrastructure
{
    [Serializable]
    public class WithActivityLog : OnMethodBoundaryAspect
    {
        Type m_currentType = null;
        ParameterInfo[] m_parameters = null;
        string m_methodName = string.Empty;
        string m_connectionString = string.Empty;
        string m_user = string.Empty;
        string m_inputParameters = string.Empty;

        public string ConnectionString
        {
            get { return m_connectionString; }
            set { m_connectionString = value; }
        }

        public WithActivityLog()
        {

        }

        public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
        {
            base.CompileTimeInitialize(method, aspectInfo);

            m_currentType = method.DeclaringType;

            m_methodName = method.DeclaringType.FullName + "." + method.Name;

            m_parameters = method.GetParameters();
        }

        // Invoked only once at runtime from the static constructor of type declaring the target method.
        public override void RuntimeInitialize(MethodBase method)
        {
            //string methodName = method.DeclaringType.FullName + "." + method.Name;
        }

        public override void OnEntry(MethodExecutionArgs args)
        {
            Thread.BeginThreadAffinity();

            args.MethodExecutionTag = Stopwatch.StartNew();
            
            //m_user = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
            if (System.Web.HttpContext.Current != null)
            {
                System.Security.Principal.IPrincipal m_user2 = System.Web.HttpContext.Current.User;
                m_user = m_user2.Identity.Name;
            }
            else
            {
                m_user = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
            }
            
            /* cattura i parametri di input al metodo (anche complessi) */
            Dictionary<string, object> m_args = new Dictionary<string, object>();

            for (int i = 0; i < args.Arguments.Count(); i++)
            {
                string name = args.Method.GetParameters()[i].Name;
                object obj = args.Arguments.GetArgument(i);

                m_args.Add(name, obj);
            }

            m_inputParameters = JsonConvert.SerializeObject(m_args);
            /* cattura i parametri di input al metodo (anche complessi) */
        }

        public override void OnExit(MethodExecutionArgs args)
        {
            var sw = (Stopwatch)args.MethodExecutionTag;
            sw.Stop();

            Thread.EndThreadAffinity();

            string m_msgErr = string.Empty;
            if (args.Exception != null)
            {
                m_msgErr = args.Exception.Message;
            }

            //
            string query = "INSERT INTO dbo.trace_activities (trace, username, nome_metodo, parametri) VALUES (@trace, @username, @nome_metodo, @parametri)";

            string m_cn = ConfigurationManager.ConnectionStrings[m_connectionString].ConnectionString;
            // create connection and command
            using (SqlConnection cn = new SqlConnection(m_cn))
            using (SqlCommand cmd = new SqlCommand(query, cn))
            {
                // define parameters and their values
                cmd.Parameters.Add("@trace", SqlDbType.VarChar, 100).Value = string.Format("{0} [{1} - {2}ms] {3}", args.Method.Name, sw.Elapsed, sw.ElapsedMilliseconds, m_msgErr);
                cmd.Parameters.Add("@username", SqlDbType.VarChar, 50).Value = m_user;
                cmd.Parameters.Add("@nome_metodo", SqlDbType.VarChar, 150).Value = m_methodName;
                cmd.Parameters.Add("@parametri", SqlDbType.VarChar, -1).Value = m_inputParameters;

                // open connection, execute INSERT, close connection
                cn.Open();
                cmd.ExecuteNonQuery();
                cn.Close();
            }
        }
    }
}
